﻿using System;
using System.Collections.ObjectModel;
using System.Runtime.InteropServices;
using System.Text;
using SCG = System.Collections.Generic;

using GASS.OpenCL;
using GASS.OpenCL.OpenCLDriver;

namespace Nemerle.OpenCL.Utils {

    module Converter {

        this() {
            ConcreteConverter.[string].Convert = fun(buffer, size : SizeT) {
                Encoding.ASCII.GetString(buffer, 0, size - 1)
            }
            ConcreteConverter.[int].Convert = fun(buffer, _) {
                BitConverter.ToInt32(buffer, 0)
            }
            ConcreteConverter.[uint].Convert = fun(buffer, _) {
                BitConverter.ToUInt32(buffer, 0)
            }
            ConcreteConverter.[long].Convert = fun(buffer, _) {
                BitConverter.ToInt64(buffer, 0)
            }
            ConcreteConverter.[ulong].Convert = fun(buffer, _) {
                BitConverter.ToUInt64(buffer, 0)
            }
            ConcreteConverter.[bool].Convert = fun(buffer, _) {
                BitConverter.ToUInt32(buffer, 0) != 0
            }
            ConcreteConverter.[IntPtr].Convert = 
                if(IntPtr.Size == 8) // 64-bit mode
                    (buffer, _) => IntPtr(BitConverter.ToInt64(buffer, 0))
                else
                    (buffer, _) => IntPtr(BitConverter.ToInt32(buffer, 0));

            ConcreteConverter.[NDRange].Convert = fun(buffer, size : SizeT) {
                def result = array(size / IntPtr.Size);
                fixed(ptr = result)
                    Marshal.Copy(buffer, 0, ptr, size);
                result
            }
            ConcreteConverter.[ReadOnlyCollection.[string]].Convert = fun(buffer, size : SizeT) {
                def s = Encoding.ASCII.GetString(buffer, 0, size);
                Array.AsReadOnly(s.Split(array[" "], StringSplitOptions.RemoveEmptyEntries))
            }
            ConcreteConverter.[CLBuildStatus].Convert = fun(buffer, _) {
                BitConverter.ToInt32(buffer, 0) :> CLBuildStatus
            }
            ConcreteConverter.[CLDeviceExecCapabilities].Convert = fun(buffer, _) {
                BitConverter.ToUInt64(buffer, 0) :> CLDeviceExecCapabilities
            }
            ConcreteConverter.[CLDeviceMemCacheType].Convert = fun(buffer, _) {
                BitConverter.ToUInt32(buffer, 0) :> CLDeviceMemCacheType
            }
            ConcreteConverter.[CLDeviceLocalMemType].Convert = fun(buffer, _) {
                BitConverter.ToUInt32(buffer, 0) :> CLDeviceLocalMemType
            }
            ConcreteConverter.[CLDeviceType].Convert = fun(buffer, _) {
                BitConverter.ToUInt64(buffer, 0) :> CLDeviceType
            }
            ConcreteConverter.[CLDeviceFPConfig].Convert = fun(buffer, _) {
                BitConverter.ToUInt64(buffer, 0) :> CLDeviceFPConfig
            }
            ConcreteConverter.[CLExecutionStatus].Convert = fun(buffer, _) {
                BitConverter.ToUInt32(buffer, 0) :> CLExecutionStatus
            }
            ConcreteConverter.[CLCommandType].Convert = fun(buffer, _) {
                BitConverter.ToUInt32(buffer, 0) :> CLCommandType
            }
            ConcreteConverter.[CLCommandQueueProperties].Convert = fun(buffer, _) {
                BitConverter.ToUInt64(buffer, 0) :> CLCommandQueueProperties
            }
            ConcreteConverter.[CLMemObjectType].Convert = fun(buffer, _) {
                BitConverter.ToUInt32(buffer, 0) :> CLMemObjectType
            }
        }

        public Convert[T](buffer : array[byte], size : SizeT) : T {
            ConcreteConverter.Convert(buffer, size);
        }

        private module ConcreteConverter[T] {
            public mutable Convert : array[byte] * SizeT -> T =
                (_,_) => throw NotSupportedException($"There is no CL data mapper for type $(typeof(T)).");
        }

        public Convert(b : bool) : CLBool {
            | true => CLBool.True
            | _ => CLBool.False
        }

    }

}
